package com.example.transaction.service;

import com.example.transaction.exception.InvalidTransactionException;
import com.example.transaction.exception.TransactionNotFoundException;
import com.example.transaction.model.Transaction;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class TransactionService {
    private final Map<String, Transaction> transactions = new ConcurrentHashMap<>();

    @CacheEvict(value = "transactions", allEntries = true)
    public Transaction create(Transaction transaction) {
        validateTransaction(transaction);
        String id = UUID.randomUUID().toString();
        transaction.setId(id);
        transaction.setTimestamp(LocalDateTime.now());
        transactions.put(id, transaction);
        return transaction;
    }

    @CacheEvict(value = "transactions", allEntries = true)
    public void delete(String id) {
        if (!transactions.containsKey(id)) {
            throw new TransactionNotFoundException("Transaction not found with ID: " + id);
        }
        transactions.remove(id);
    }

    @CacheEvict(value = "transactions", allEntries = true)
    public Transaction update(String id, Transaction transaction) {
        if (!transactions.containsKey(id)) {
            throw new TransactionNotFoundException("Transaction not found with ID: " + id);
        }
        validateTransaction(transaction);
        transaction.setId(id);
        transactions.put(id, transaction);
        return transaction;
    }

    @Cacheable("transactions")
    public Page<Transaction> list(int page, int size) {
        List<Transaction> allTransactions = new ArrayList<>(transactions.values());
        int start = Math.min(page * size, allTransactions.size());
        int end = Math.min((page + 1) * size, allTransactions.size());
        return new PageImpl<>(
                allTransactions.subList(start, end),
                PageRequest.of(page, size),
                allTransactions.size()
        );
    }

    private void validateTransaction(Transaction transaction) {
        if (transaction.getAmount().compareTo(BigDecimal.ZERO) < 0) {
            throw new InvalidTransactionException("Amount cannot be negative");
        }
        if (!Set.of("INCOME", "EXPENSE").contains(transaction.getType())) {
            throw new InvalidTransactionException("Invalid transaction type. Allowed: INCOME/EXPENSE");
        }
    }
}